home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
9-Digit Zip Code Directory
/
9-Digit Zip Code Directory (American Business Information) (ABIZIP-12).ISO
/
z4src.zip
/
DIREAD.C
< prev
next >
Wrap
C/C++ Source or Header
|
1995-09-14
|
12KB
|
355 lines
//----------------------------------------------------------------------------
// MODULE DESCRIPTION
//
// Module: diread.c
// Title: Data File I/O Library
// Notice: John M. Weeder
// Copyright (c) 1993. All rights reserved.
// This module contains proprietary information and should be
// treated as confidential.
//
//----------------------------------------------------------------------------
// MAINTENANCE HISTORY
//
// $Workfile$
// $Revision$
// $Author$
// $Date$
// $Log$
//
//----------------------------------------------------------------------------
// MODULE NARRATIVE
//
//
// This module contains code to read from a data file.
//
// The code in this module should be written entirely in C.
// Do not use any C++ constructs.
//
// This module is portable to:
// DOS 3.X+
// MS Windows 3.X+
// OS/2 2.X+
// OS/2 2.0 PM
// SCO UNIX.
//
// The following compilers are supported:
// MSC 6.0A
// MSC/C++ 7.0
// Borland C++ 3.1 for DOS
// Borland C++ 1.0 for OS/2 2.X
// SCO UNIX cc
//
//----------------------------------------------------------------------------
#include <di.h>
//----------------------------------------------------------------------------
// Prototypes
//----------------------------------------------------------------------------
static VOID FN_L DioReadSearch(PBYTE, SIZET, PBYTE, SIZET, PLONG);
//----------------------------------------------------------------------------
// Description: Read from a random access file.
// Parameters: hlf Logical file handle.
// pb Buffer to read.
// cb Size of buffer to read.
// pcb If not null, this variable recieves the number of
// bytes read.
// Returns: TRUE if successful.
//----------------------------------------------------------------------------
BOOL FN_E DioRead(HLF hlf, PBYTE pb, SIZET cb, PSIZET pcb)
{
FPOS fpos, flen;
if (pcb)
*pcb = 0;
Assert(hlf >= 0 && hlf < MAX_LOGICAL_FILES);
Assert(di.logical[hlf].fUsed);
Assert(pb && cb);
if ((di.logical[hlf].fl & (LF_ERROR|LF_EOF))
|| (di.physical[di.logical[hlf].hpf].fl & LF_ERROR))
return FALSE;
fpos = di.logical[hlf].fpos; // Get actual offset into file and
flen = di.logical[hlf].flen - fpos; // adjust length if needed.
if ((FPOS)cb > flen)
cb = (SIZET)flen;
fpos += di.logical[hlf].fbase; // Read data
if (!FileRead(di.logical[hlf].hf, pb, cb, fpos))
{
di.logical[hlf].fl |= LF_ERROR; // If failed, set error flag
return FALSE;
}
di.logical[hlf].fpos += (FPOS)cb; // Move file pointer
if (di.logical[hlf].fpos >= di.logical[hlf].flen)
di.logical[hlf].fl |= LF_EOF;
if (pcb)
*pcb = cb;
return TRUE;
}
//----------------------------------------------------------------------------
// Description: Read a block.
// This function also handles caching on a block by block
// basis.
// Parameters: hlf Logical file handle
// pb Buffer to read block into.
// If null, the data is simply placed in the cache
// if one is present.
// Buffer is assumed to be of correct size
// ppb If not null, the variable recieves a pointer to
// the cache buffer containing the data which was
// read.
// plBlock If not null, this variable recieves the id of the
// block which was read.
// Returns: TRUE if successful.
//----------------------------------------------------------------------------
BOOL FN_E DioReadBlock(HLF hlf, PBYTE pb, PBYTE _FAR_ *ppb, PLONG plBlock)
{
SIZET cCache; // Number of cache buffers
LONG lBlock; // Block id being read
SIZET cBlock; // Size of a block
SIZET cbRead; // Number of bytes read
PCACHE pcacheReplace = NULL; // New cache buffer for data
PCACHE pcacheFound = NULL; // Cache buffer with data
PCACHE pcache;
//
// First, perform a bunch of parameter validation in case something
// has failed.
//
Assert(hlf >= 0 && hlf < MAX_LOGICAL_FILES);
Assert(di.logical[hlf].fUsed);
Assert(di.logical[hlf].usBlockSize);
Assert((di.logical[hlf].fpos % (FPOS)di.logical[hlf].usBlockSize) == 0);
Assert(pb || ppb);
//
// Search through the cache looking for the block being read.
// If found, return data and set usage for buffer to 1.
// Increment the usage counter of all other used cache buffers.
// At the same time, keep tracked of the oldest cache buffer in
// cache we need to read data into it.
//
cBlock = (SIZET)di.logical[hlf].usBlockSize;
lBlock = (LONG)(di.logical[hlf].fpos / (FPOS)di.logical[hlf].usBlockSize);
cCache = di.logical[hlf].cCache;
if (plBlock)
*plBlock = lBlock;
if (cCache)
{
PBYTE pbCache = di.logical[hlf].pcache;
USHORT usUsage = 1;
for (; cCache; --cCache)
{
pcache = (PCACHE)pbCache;
if (pcache->usUsage)
{ // Is this the block we want?
if (pcache->lBlock == lBlock)
pcacheFound = pcache; // If so, save a pointer to it
else if (pcache->usUsage < 0xFFFF)
pcache->usUsage++; // Otherwise, bump usage count
}
// Watch for oldest or unused buffer
if (usUsage && (!pcache->usUsage || pcache->usUsage >= usUsage))
{
usUsage = pcache->usUsage;
pcacheReplace = pcache;
}
pbCache += sizeof(CACHE) - sizeof(BYTE) + cBlock;
}
if (pcacheFound) // If data was found in the cache
{ // Set usage count to 1 and return
if (ppb) // data to the user
*ppb = pcacheFound->bBuffer;
if (pb)
memcpy(pb, pcacheFound->bBuffer, cBlock);
pcacheFound->usUsage = 1;
return TRUE;
}
}
//
// Temporarily mark cache buffer as unused.
// Then read the data.
//
if (pb == NULL) // Make sure there is a buffer to
{ // read into
Assert(pcacheReplace);
pb = pcacheReplace->bBuffer;
}
if (pcacheReplace)
pcacheReplace->usUsage = 0;
if (!DioRead(hlf, pb, cBlock, &cbRead))
return FALSE;
if (cBlock != cbRead) // If failed, set error flags
{
di.logical[hlf].fl |= LF_ERROR;
return FALSE;
}
//
// Update cache.
//
if (ppb)
{
if (pcacheReplace)
*ppb = pcacheReplace->bBuffer;
else
*ppb = pb;
}
if (pcacheReplace)
{
pcacheReplace->usUsage = 1; // Update cache data
pcacheReplace->lBlock = lBlock;
// Copy data to cache buffer
if (pb != pcacheReplace->bBuffer)
memcpy(pcacheReplace->bBuffer, pb, cBlock);
}
return TRUE;
}
//----------------------------------------------------------------------------
// Description: Read first keyed record block.
// After the first read, the DioReadBlock() function may be
// called to read successive blocks.
// Parameters: hlf Logical file handle
// pbKey Key data
// cbKey Size of key
// pb Buffer to read block into.
// If null, the buffer is simply placed in the cache
// if one is present.
// Buffer is assumed to be of correct size
// ppb If not null, the variable recieves a pointer to
// the cache buffer containing the data which was
// read.
// plBlock If not null, this variable recieves the id of the
// block which was read.
// Returns: TRUE if successful.
//----------------------------------------------------------------------------
BOOL FN_E DioReadFirst(HLF hlf, PBYTE pbKey, SIZET cbKey, PBYTE pb, PBYTE _FAR_ *ppb, PLONG plBlock)
{
LONG lBlock;
if (!DioReadIndex(hlf, pbKey, cbKey, &lBlock))
return FALSE;
if (plBlock)
*plBlock = lBlock;
Assert(pb || ppb);
if (!DioSeekBlock(hlf, lBlock)
|| !DioReadBlock(hlf, pb, ppb, &lBlock))
return FALSE;
return TRUE;
}
//----------------------------------------------------------------------------
// Description: Read index and find the block id of the block containing
// the record matching the specified key.
// Parameters: hlf Logical file handle
// pbKey Key data
// cbKey Size of key
// plBlock If not null, this variable recieves the id of the
// block which was read.
// Returns: TRUE if successful.
//----------------------------------------------------------------------------
BOOL FN_E DioReadIndex(HLF hlf, PBYTE pbKey, SIZET cbKey, PLONG plBlock)
{
HLF hlfNext;
PBYTE pbBlock;
LONG lBlock;
SIZET cBlock;
//
// First, perform a bunch of parameter validation in case something
// has failed.
//
Assert(hlf >= 0 && hlf < MAX_LOGICAL_FILES);
Assert(di.logical[hlf].fUsed);
Assert(di.logical[hlf].usBlockSize);
Assert(di.logical[hlf].usType == DFT_ISAM_DATA);
Assert(di.logical[hlf].hlfNext >= 0);
Assert((di.logical[hlf].fpos % (FPOS)di.logical[hlf].usBlockSize) == 0);
Assert(pbKey && cbKey);
Assert(plBlock);
//
// The data file pointer points to the top level isam index.
// Read down the isam tree from the top until the bottom level.
//
hlfNext = di.logical[hlf].hlfNext;
lBlock = 0;
while (hlfNext >= 0)
{
if (!DioSeekBlock(hlfNext, lBlock)
|| !DioReadBlock(hlfNext, NULL, &pbBlock, NULL))
return FALSE;
cBlock = (SIZET)di.logical[hlfNext].usBlockSize;
DioReadSearch(pbBlock, cBlock, pbKey, cbKey, &lBlock);
hlfNext = di.logical[hlfNext].hlfNext;
}
*plBlock = lBlock;
return TRUE;
}
//----------------------------------------------------------------------------
// Description: Search trough a block for a specified key.
// Parameters: pb Block to search
// cb Size of block
// pbKey Key data
// cbKey Size of key
// plBlock Variable to recieve block to read next.
// Returns:
//----------------------------------------------------------------------------
static VOID FN_L DioReadSearch(PBYTE pb, SIZET cb, PBYTE pbKey, SIZET cbKey, PLONG plBlock)
{
PISAMENTRY pe1, pe2;
int rc;
SIZET cbEntry;
pe1 = (PISAMENTRY)pb; // Is it the first key?
Assert((size_t)((PBYTE)&pe1->lBlock - pb) == 1);
Assert(sizeof(ISAMENTRY) == sizeof(BYTE) + sizeof(LONG) + ISAM_KEY_LEN);
rc = memcmp(pbKey, pe1->bKey, MIN(cbKey, (SIZET)pe1->bSize));
if (rc < 0 || (rc == 0 && (SIZET)pe1->bSize >= cbKey))
{
*plBlock = pe1->lBlock;
return ;
}
for (;;)
{
cbEntry = sizeof(ISAMENTRY) - ISAM_KEY_LEN + pe1->bSize;
Assert(cbEntry <= cb); // Should never happen
if (cbEntry == cb) // Last key in this block
break;
cb -= cbEntry;
pb += cbEntry; // Move to next key
pe2 = (PISAMENTRY)pb;
if (pe2->bSize == 0) // Another way of specifying the last
break; // key in a block
//
// If the inquiry key is less than the next key select this block.
// If it is equal, and the index key is >= select this block. This
// covers case like NE_LINDSAY --> NE_L where the key is not unique
// and may span blocks.
//
rc = memcmp(pbKey, pe2->bKey, MIN(cbKey, (SIZET)pe2->bSize));
if (rc < 0 || (rc == 0 && (SIZET)pe2->bSize >= cbKey))
break; // Less than next key, so done!
pe1 = pe2;
}
*plBlock = pe1->lBlock;
return ;
}
//----------------------------------------------------------------------------
//------------------------------- End of File --------------------------------
//----------------------------------------------------------------------------